package cease.console;

import java.io.Console;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;

import org.apache.commons.lang.StringUtils;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.Option;

import cease.butter.ApiException;
import cease.butter.Constants;
import cease.command.Command;
import cease.command.HasOptionCommand;
import cease.command.block.BlockCommand;
import cease.command.block.CheckBlockCommand;
import cease.command.block.UnblockCommand;
import cease.command.butter.DelButterCommand;
import cease.command.butter.MuteButterCommand;
import cease.command.butter.PostButterCommand;
import cease.command.butter.RetweetCommand;
import cease.command.butter.ShowButterCommand;
import cease.command.butter.UnmuteButterCommand;
import cease.command.cease.DownloadAppCommand;
import cease.command.cease.LogoutCommand;
import cease.command.cease.QuitCommand;
import cease.command.cease.VersionCommand;
import cease.command.favorite.AddFavoriteCommand;
import cease.command.favorite.DelFavoriteCommand;
import cease.command.favorite.ShowFavoriteCommand;
import cease.command.friendship.FollowCommand;
import cease.command.friendship.UnfollowCommand;
import cease.command.message.DelMessageCommand;
import cease.command.message.SendMessageCommand;
import cease.command.message.ShowMessagesCommand;
import cease.command.photo.PhotoUploadCommand;
import cease.command.timeline.SearchTLCommand;
import cease.command.timeline.ShowHomeTLCommand;
import cease.command.timeline.ShowMentionTLCommand;
import cease.command.timeline.ShowPublicTLCommand;
import cease.command.timeline.ShowRetweetTLCommand;
import cease.command.timeline.ShowUserTLCommand;
import cease.urs.LoginException;
import cease.urs.Loginer;
import cease.urs.Session;
import cease.util.CmdLineArgsParser;

/**
 * @author dhf
 */
public class Cease extends HasOptionCommand {
    @Option(name = "-c", metaVar = "optional", usage = "show specified command usage")
    private String cmd;

    private static Map<String, Command> commands = new LinkedHashMap<String, Command>(
            50);
    static {
        commands.put(Config.KEY_TIMELINE_HOME, new ShowHomeTLCommand());
        commands.put(Config.KEY_TIMELINE_MENTION, new ShowMentionTLCommand());
        commands.put(Config.KEY_TIMELINE_RETWEET, new ShowRetweetTLCommand());
        commands.put(Config.KEY_TIMELINE_USER, new ShowUserTLCommand());
        commands.put(Config.KEY_TIMELINE_PUBLIC, new ShowPublicTLCommand());
        commands.put(Config.KEY_TIMELINE_SEARCH, new SearchTLCommand());

        commands.put(Config.KEY_FAVORITE_SHOW, new ShowFavoriteCommand());
        commands.put(Config.KEY_FAVORITE_ADD, new AddFavoriteCommand());
        commands.put(Config.KEY_FAVORITE_DELETE, new DelFavoriteCommand());

        commands.put(Config.KEY_BUTTER_POST, new PostButterCommand());
        commands.put(Config.KEY_BUTTER_RETWEET, new RetweetCommand());
        commands.put(Config.KEY_BUTTER_DELETE, new DelButterCommand());
        commands.put(Config.KEY_BUTTER_SHOW, new ShowButterCommand());
        commands.put(Config.KEY_BUTTER_MUTE, new MuteButterCommand());
        commands.put(Config.KEY_BUTTER_UNMUTE, new UnmuteButterCommand());

        commands.put(Config.KEY_BLOCK_ADD, new BlockCommand());
        commands.put(Config.KEY_BLOCK_DELETE, new UnblockCommand());
        commands.put(Config.KEY_BLOCK_CHECK, new CheckBlockCommand());

        commands.put(Config.KEY_FRIENDSHIP_FOLLOW, new FollowCommand());
        commands.put(Config.KEY_FRIENDSHIP_UNFOLLOW, new UnfollowCommand());

        commands.put(Config.KEY_PHOTO_UPLOAD, new PhotoUploadCommand());

        commands.put(Config.KEY_MESSAGE_CREATE, new SendMessageCommand());
        commands.put(Config.KEY_MESSAGE_DELETE, new DelMessageCommand());
        commands.put(Config.KEY_MESSAGE_SHOW, new ShowMessagesCommand());

        commands.put(Config.KEY_CEASE_DOWNLOAD, new DownloadAppCommand());
        commands.put(Config.KEY_CEASE_VERSION, new VersionCommand());
        commands.put(Config.KEY_HELP, new Cease());
        commands.put(Config.KEY_QUIT, new QuitCommand());
        commands.put(Config.KEY_LOGOUT, new LogoutCommand());
    }

    public static void main(String[] args) throws Exception {
        Session session = login(args);
        Scanner scanner = new Scanner(System.in);
        String line = null;
        System.out.println("Command list:");
        showCommandList();
        System.out.print(Constants.APP_NAME + ">");
        while (null != (line = scanner.nextLine())) {
            String[] splits = CmdLineArgsParser.parse(line);
            String cmd = splits[0];
            Command command = commands.get(cmd);
            String[] cmdArgs = new String[splits.length - 1];
            if (splits.length > 1) {
                for (int i = 1; i < splits.length; i++) {
                    cmdArgs[i - 1] = splits[i];
                }
            }

            if (null == command) {
                System.out.println("command: [" + cmd + "] not supported");
            } else {
                try {
                    command.run(session, cmdArgs);
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
            }
            System.out.print(Constants.APP_NAME + ">");
        }
    }

    private static Session login(String[] args) {
        Session session = null;
        String username = null;
        String password = null;
        if (args.length < 2) {
            // try to read from cookie file
            File f = new File(Config.sessionFile);
            if (f.exists() && f.canRead()) {
                InputStream is = null;
                try {
                    is = new FileInputStream(f);
                    session = Session.readIn(is);
                } catch (Exception e) {} finally {
                    try {
                        if (null != is) {
                            is.close();
                        }
                    } catch (Exception e) {}
                }
            }
            if (null == session
                    || System.currentTimeMillis() - session.getCreateTime() > Config.sessionTimeout * 1000) {
                // read fail or session timeout, ask for username && password
                if (Config.echoPassword) {
                    Scanner scanner = new Scanner(System.in);
                    if (args.length <= 0) {
                        System.out.println(Constants.APP_NAME + ">"
                                + "please input your username:");
                        username = scanner.nextLine();
                    } else {
                        username = args[0];
                    }
                    System.out.println(Constants.APP_NAME + ">"
                            + "please input your password:");
                    password = scanner.nextLine();
                } else {
                    Console console = System.console();
                    if (null == console) {
                        throw new IllegalStateException(
                                "STDIN has been redirected");
                    }
                    if (args.length <= 0) {
                        username = console.readLine(Constants.APP_NAME + ">"
                                + "please input your username:");
                    } else {
                        username = args[0];
                    }
                    password = new String(
                            console.readPassword(Constants.APP_NAME + ">"
                                    + "please input your password:"));
                }
            } else {
                return session;
            }
        } else {
            username = args[0];
            password = args[1];
        }
        try {
            session = Loginer.login(username, password);
        } catch (LoginException e) {
            System.out.println(e.getMessage());
            exit(2);
        }
        password = null;
        if (null == session) {
            System.out.println("password wrong");
            exit(2);
        }
        if (Config.saveSession) {
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(Config.sessionFile);
                Session.writeOut(session, fos);
            } catch (Exception e) {} finally {
                if (null != fos) {
                    try {
                        fos.close();
                    } catch (Exception e) {}
                }
            }
        }
        return session;
    }

    public static void exit(int status) {
        System.exit(status);
    }

    public static void showCommandList() {
        for (Entry<String, Command> entry: commands.entrySet()) {
            String cmdName = entry.getKey();
            Command cmd = entry.getValue();
            String cmdDesc = cmd.getCmdDesc();
            String cmdOptDesc = cmd.getOptDesc();
            System.out.println("[" + cmdName + "]\t" + cmdDesc);
            if (StringUtils.isNotBlank(cmdOptDesc)) {
                System.out.println(cmdOptDesc);
            }
        }
    }

    public String getCmdDesc() {
        return "show commands usage";
    }

    private void reset() {
        cmd = null;
    }

    public void run(Session session, String[] args) throws ApiException {
        try {
            parser.parseArgument(args);
            if (null == cmd) {
                showCommandList();
            } else {
                Command command = commands.get(cmd);
                if (null == command) {
                    System.out.println("command: [" + cmd + "] not supported");
                } else {
                    System.out
                            .println("[" + cmd + "]\t" + command.getCmdDesc());
                    System.out.println(command.getOptDesc());
                }
            }
        } catch (CmdLineException e) {
            parser.printUsage(System.out);
        } finally {
            reset();
        }
    }
}
